package com.study.bindview_compiler;

import com.study.bindviewannotation.BindView;
import com.study.bindviewannotation.OnClick;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;

/**
 * 作者： zjf 10/12/20 8:07 PM
 * 描述：注解解析器,javac在编译的时候会执行这里的代码
 */
public class BindViewProcessor extends AbstractProcessor {

    /**
     * 文件相关的辅助类 用于生成新的源文件、class等
     */
    private Filer mFiler;
    private Messager messager;//打印日志使用
    private Elements mElementUtils;

    private Map<String, AnnotatedClass> annotatedClassMap = new HashMap<>();

    /**
     * 做一些初始化的工作
     *
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
        mFiler = processingEnv.getFiler();

        mElementUtils = processingEnv.getElementUtils();
    }

    /**
     * 逻辑在这里写,返回true,则表示此注解处理器能处理此注解
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        try {
            //解析BindView注解
            processBindView(roundEnv);
            //解析OnClick注解
            processOnClick(roundEnv);
        } catch (Exception e) {
            log(e.getMessage());
            return true;
        }

        try {

            for (AnnotatedClass annotatedClass : annotatedClassMap.values()) {
                annotatedClass.getCode().writeTo(mFiler);
            }

        } catch (Exception e) {
            log(e.getMessage());
        }

        annotatedClassMap.clear();
        return true;
    }

    /**
     * 解析OnClick注解
     */
    private void processOnClick(RoundEnvironment roundEnv) {
        //获取被OnClick注解的方法
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(OnClick.class);
        for (Element element : elements) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            OnClickMethod method = new OnClickMethod(element);
            annotatedClass.addMethod(method);
        }
    }

    /**
     * 解析BindView注解
     */
    private void processBindView(RoundEnvironment roundEnv) {
        //获取被BindView注解的元素
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
        for (Element element : elements) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            BindViewField field = new BindViewField(element);
            annotatedClass.addField(field);
        }
    }


    private AnnotatedClass getAnnotatedClass(Element element) {
        //获取被注解的元素所在的类
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        //获取全类名
        String qualifiedName = typeElement.getQualifiedName().toString();

        AnnotatedClass annotatedClass = annotatedClassMap.get(qualifiedName);
        if (annotatedClass == null) {
            annotatedClass = new AnnotatedClass(typeElement, mElementUtils);
            annotatedClassMap.put(qualifiedName, annotatedClass);
        }
        return annotatedClass;
    }

    /**
     * 编译时打印log数据
     */
    private void log(String msg) {
        messager.printMessage(Diagnostic.Kind.NOTE, msg);
    }

    /**
     * 添加支持的注解类型
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class.getCanonicalName());
        return annotations;
    }

    /**
     * 获取支持的java版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        //获取最新支持,避免警告信息
        return SourceVersion.latestSupported();
    }
}
